﻿<!DOCTYPE HTML>
<html lang="zh">
<head>
<title>ComCall - 语法 &amp; 使用 | AutoHotkey v2</title>
<meta name="description" content="The ComCall function calls a native COM interface method by index." />
<meta name="ahk:equiv-v1" content="AutoHotkey.htm" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link href="../static/theme.css" rel="stylesheet" type="text/css" />
<script src="../static/content.js" type="text/javascript"></script>
<script type="text/javascript">$(function(){0<=window.navigator.userAgent.toLowerCase().indexOf("ucbrowser")&&CaoNiMaDeUc()})</script>
</head>
<body>

 <h1>ComCall</h1>

 <p>通过索引调用原生 COM 接口方法.</p>

 <pre class="Syntax">Result := <span class="func">ComCall</span>(Index, ComObject <span class="optional">, Type1, Arg1, Type2, Arg2, ReturnType</span>)</pre>
<h2 id="Parameters">参数</h2>
<dl>

   <dt>Index</dt>
  <dd>
      <p>类型: <a href="../Concepts.htm#numbers">整数</a></p>
      <p>虚拟函数表中方法的索引(从零开始).</p>
      <p><em>Index</em> 对应于方法在原始接口定义中的位置. Microsoft 文档通常按字母顺序列出方法, 这是不相关的. 为了确定正确的索引, 请找到原来的接口定义. 这可能在头文件或类型库中.</p>
      <p>考虑方法继承于的父接口是很重要的. 因为所有的 COM 接口最终都是从 <a href="https://docs.microsoft.com/en-us/windows/win32/api/unknwn/nn-unknwn-iunknown">IUnknown</a> 派生出来的, 所以前三个方法总是 QueryInterface (0), AddRef (1) 和 Release (2). 例如, <em>IShellItem2</em> 是 <em>IShellItem</em> 的扩展, 它从索引 3 开始, 包含 5 个方法, 所以 <em>IShellItem2</em> 的第一个方法(GetPropertyStore) 的索引为 8.</p>
      <p class="note"><strong>提示:</strong> 对于微软定义的 COM 接口, 尝试在互联网或 Windows SDK 中搜索 "<em>IInterfaceName</em><b>Vtbl</b>" - 例如, "IUnknownVtbl". Microsoft 自己的接口定义附带这个接口的虚拟函数表的纯-C 语言定义, 它以正确的顺序显式地列出了所有方法.</p>
      <p>传递无效的索引可能会导致不确定的行为, 包括(但不限于) 程序终止.</p>
  </dd>

   <dt>ComObject</dt>
  <dd>
      <p>类型: <a href="../Concepts.htm#numbers">整数</a>或<a href="../Concepts.htm#objects">对象</a></p>
      <p>目标 COM 对象; 也就是说, 一个 COM 接口指针. 指针值可以直接传递, 也可以封装在带有 <code>Ptr</code> 属性的对象中, 如带有 VT_UNKNOWN 变量类型的 <a href="ComObject.htm">ComObj</a>.</p>
      <p>接口指针用于定位实现接口方法的虚拟函数的地址, 也作为参数传递. 这个参数通常不会在原生支持接口的语言中显式地出现, 而是在 C 言语风格的 "Vtbl" 定义中显示.</p>
      <p>传递无效指针可能导致未定义的行为, 包括(但不限于) 程序终止.</p>
  </dd>

   <dt>Type1, Arg1</dt>
  <dd>
      <p>类型: <a href="../Concepts.htm#strings">字符串</a></p>
      <p>每一对值表示要传递给方法的单个参数. 对的数量是无限的. 有关 <em>Type</em>, 请参阅 <a href="DllCall.htm#types">DllCall 类型列表</a>. 有关 <em>Arg</em>, 指定要传递给方法的值.</p>
  </dd>

   <dt>ReturnType</dt>
  <dd>
      <p>类型: <a href="../Concepts.htm#strings">字符串</a></p>
      <p>如果省略, 返回类型默认为 <a href="DllCall.htm#HRESULT">HRESULT</a>, 这是 COM 接口方法最常用的返回类型. 任何指示失败的结果都会抛出异常; 因此, 除非实际的返回类型是 HRESULT, 否则不能省略返回类型.</p>
      <p>如果方法是不返回值的类型(C 语言中的 <code>void</code> 返回类型), 则指定 "Int" 或 或不带任何后缀的其他数字类型(HRESULT 除外), 并忽略返回值. 在这种情况下, 由于返回值寄存器的内容是任意的, 所以如果省略了 <em>ReturnType</em>, 可能会也可能不会抛出异常.</p>
      <p>否则, 指定<a href="DllCall.htm#types">DllCall 类型列表</a>中的一个参数类型. 支持<a href="DllCall.htm#asterisk">星号后缀</a>.</p>
      <p id="cdecl">尽管 ComCall 和 <a href="DllCall.htm#cdecl">DllCall</a> 支持 <em>Cdecl</em>, 但它通常不在 COM 接口方法中使用.</p>
  </dd>

 </dl>

 <h2 id="Return_Value">返回值</h2>
<p>类型: <a href="../Concepts.htm#strings">字符串</a>或<a href="../Concepts.htm#numbers">整数</a></p>
<p>如果 <em>ReturnType</em> 是 <a href="DllCall.htm#HRESULT">HRESULT</a>(或省略), 并且方法返回一个错误值(由 <a href="https://docs.microsoft.com/en-us/windows/win32/api/winerror/nf-winerror-failed">FAILED macro</a> 定义), 则抛出异常.</p>
<p>否则, ComCall 将返回该方法返回的实际值. 如果方法是不返回值的类型(该返回类型在 C 语言中定义为 <code>void</code>), 则结果是未定义的, 应将其忽略.</p>

 <h2 id="Remarks">备注</h2>
<p>以下 DllCall 主题也适用于 ComCall:</p>
<ul>
  <li><a href="DllCall.htm#types">Types of Arguments and Return Values</a></li>
  <li><a href="DllCall.htm#error">Errors</a></li>
  <li><a href="DllCall.htm#except">Native Exceptions and A_LastError</a></li>
  <li><a href="DllCall.htm#struct">Structures and Arrays</a></li>
  <li><a href="DllCall.htm#limits">Known Limitations</a></li>
  <li><a href="DllCall.htm#dotnet">.NET Framework</a></li>
</ul>

 <h2 id="Related">相关</h2>
<p><a href="ComObjCreate.htm">ComObjCreate</a>, <a href="ComObjQuery.htm">ComObjQuery</a>, <a href="ComObject.htm">ComObject</a>, <a href="BufferAlloc.htm">BufferAlloc</a>, <a href="../objects/Buffer.htm">Buffer object</a>, <a href="CallbackCreate.htm">CallbackCreate</a></p>

 <h2 id="Examples">示例</h2>

 <div class="ex" id="ExTaskbar">
<p><a href="#ExTaskbar">#1</a>: 从任务栏中删除活动窗口 3 秒钟. 可以与<a href="DllCall.htm#ExTaskbar">等效的 DllCall 示例</a>进行比较.</p>
<pre><em>/*
  <a href="http://msdn.microsoft.com/en-us/library/bb774652.aspx">ITaskbarList</a> 虚拟函数列表中的方法:
    IUnknown:
      0 QueryInterface  -- 使用 <a href="ComObjQuery.htm">ComObjQuery</a> 代替
      1 AddRef          -- use <a href="ObjAddRef.htm">ObjAddRef</a> instead
      2 Release         -- use <a href="ObjAddRef.htm">ObjRelease</a> instead
    ITaskbarList:
      3 HrInit
      4 AddTab
      5 DeleteTab
      6 ActivateTab
      7 SetActiveAlt
*/</em>
IID_ITaskbarList  := "{56FDF342-FD6D-11d0-958A-006097C9A090}"
CLSID_TaskbarList := "{56FDF344-FD6D-11d0-958A-006097C9A090}"

<em>; 创建 TaskbarList 对象并将其地址存储在 tbl 中.</em>
tbl := <a href="ComObjCreate.htm">ComObjCreate</a>(CLSID_TaskbarList, IID_ITaskbarList)

<em>; 由于上面指定了 IID, tbl 是指针(一个整数).
; 指针可以包装起来以便自动释放
; 当脚本不再具有对包装对象的引用时.
; 如果删除以下行, 示例仍然有效:</em>
tbl := ComObject(13, tbl)

activeHwnd := WinExist("A")

ComCall(3, tbl)                     <em>; tbl.<a href="http://msdn.microsoft.com/en-us/library/bb774650.aspx">HrInit</a>()</em>
ComCall(5, tbl, "ptr", activeHwnd)  <em>; tbl.<a href="http://msdn.microsoft.com/en-us/library/bb774648.aspx">DeleteTab</a>(activeHwnd)</em>
Sleep 3000
ComCall(4, tbl, "ptr", activeHwnd)  <em>; tbl.<a href="http://msdn.microsoft.com/en-us/library/bb774646.aspx">AddTab</a>(activeHwnd)</em>

if IsObject(tbl)  <em>; 是否是 ComObject(13, tbl)?</em>
{
    <em>; 完成对象后, 只需将所有引用替换为
    ; 其他一些值(如果是局部变量, 就返回):</em>
    tbl := ""
}
else
{
    <em>; 因为指针没有被包装, 所以必须手动释放它
    ; (虽然任何对象或相应的库使用的内存
    ; 当程序退出时, 将被回收).</em>
    ObjRelease(tbl)
}
</pre>
</div>

<div class="ex" id="ExTaskbarClass">
<p><a href="#ExTaskbarClass">#2</a>: 演示一些包装 COM 接口的技术. 等同于上一个示例.</p>
<pre>
tbl := TaskbarList.new()

activeHwnd := WinExist("A")

tbl.DeleteTab(activeHwnd)
Sleep 3000
tbl.AddTab(activeHwnd)

tbl := ""


class TaskbarList {
   static IID := "{56FDF342-FD6D-11d0-958A-006097C9A090}"
   static CLSID := "{56FDF344-FD6D-11d0-958A-006097C9A090}"

   <em>; 在启动时调用, 以初始化类.</em>
   static __new() {
       <em>; 获取 TaskbarList 的所有实例的基对象.</em>
       proto := this.Prototype

       <em>; 绑定函数可用于预定义参数, 使
       ; 这些方法在不需要包装器函数的情况下更容易使用.
       ; HrInit 本身没有参数, 所以只绑定索引,
       ; 而调用者将隐式地提供 'this'.</em>
       proto.DefineMethod "HrInit", Func("ComCall").Bind(3)

       <em>; 将参数留空, 让调用者提供一个值.
       ; 在本例中, 空白参数是 'this'(通常是隐藏的).</em>
       proto.DefineMethod "AddTab", Func("ComCall").Bind(4,, "ptr")

       <em>; 可以使用对象或映射来减少重复.</em>
       for name, args in Map(
           "DeleteTab", [5,,"ptr"],
           "ActivateTab", [6,,"ptr"],
           "SetActiveAlt", [7,,"ptr"]) {
           proto.DefineMethod name, Func("ComCall").Bind(args*)
       }
   }

   <em>; 在新实例上由 TaskbarList.new() 调用.</em>
   __new() {
        this.comobj := ComObjCreate(TaskbarList.CLSID, TaskbarList.IID)
        this.ptr := this.comobj.ptr
       <em>; 通过 ITaskbarList 请求初始化.</em>
       this.HrInit()
   }
}
</pre>
</div>

</body>
</html>